package lion.prototol;

import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.management.RuntimeErrorException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class GeneratorJava {

	private static Logger logger = LoggerFactory.getLogger(GeneratorJava.class);

	private static Map<Integer, Integer> REQ_ALL_PROTOCOL_ID_MAP = new HashMap<>();

	public static void generate(String protocolPath) throws JsonParseException, JsonMappingException, IOException {
		File rootDir = new File(protocolPath);
		File[] filenames = rootDir.listFiles(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				if (name.contains("MyProtocols")) {
					return false;
				}
				return name.contains(".json");
				// return name.contains("bag.js");
			}
		});
		ObjectMapper mapper = new ObjectMapper();
		for (File jsonFile : filenames) {
			String fileName = jsonFile.getName();
			fileName = StringUtils.remove(fileName, ".json");
			logger.info("fileName={}", upperCase(fileName));
			Map<String, Object> templateMap = mapper.readValue(jsonFile, Map.class);
			buildMessage(fileName, templateMap);
		}
	}

	private static void buildMessage(String fileName, Map<String, Object> templateMap) throws IOException {
		fileName = upperCase(fileName);
		StringBuilder sb = new StringBuilder();
		sb.append("package lua;\n");
		sb.append("import io.netty.buffer.ByteBufAllocator;\n");
		sb.append("import io.netty.buffer.ByteBuf;\n");
		sb.append("import lion.netty4.message.RequestMessageRaw;\n");
		sb.append("import lion.netty4.message.SendToMessageRaw;\n");
		sb.append("public final class LuaMessage").append(fileName).append("{\n");
		for (Map.Entry<String, Object> aEntry : templateMap.entrySet()) {
			String aCalssName = aEntry.getKey();
			Map<String, Object> aObj = (Map<String, Object>) aEntry.getValue();
			sb.append("\tpublic static final class ").append(aCalssName).append("{\n");
			List<Map<String, String>> fieldsObj = (List<Map<String, String>>) aObj.get("fields");
			// 字段
			if (fieldsObj != null && fieldsObj.size() > 0) {
				for (Map<String, String> aFileConfig : fieldsObj) {
					aFileConfig.remove("desc");
					for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
						String fieldName = bEntry.getKey();
						String fieldType = bEntry.getValue();
						String javaTypeName = getJavaTypeName(fieldType);
						sb.append("\t\tpublic ").append(javaTypeName).append(" ").append(fieldName).append(";\n");
						// sb.append("\t\tpublic " + javaTypeName + " get" +
						// upperCase(fieldName) + "(){\n");
						// sb.append("\t\t\treturn " + fieldName + ";\n");
						// sb.append("\t\t}\n");
					}
				}
			}
			// 构造函数
			if (aObj.get("is_req") == null || !(boolean) aObj.get("is_req")) {
				boolean isFieldRaw = true;
				if (fieldsObj != null && fieldsObj.size() > 0) {
					Map<String, String> fieldPair = new HashMap<>();
					for (Map<String, String> aFileConfig : fieldsObj) {
						aFileConfig.remove("desc");
						for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
							String fieldType = bEntry.getValue();
							fieldPair.put(bEntry.getKey(), fieldType);
							isFieldRaw = isFieldRaw(fieldType);
							if (!isFieldRaw) {
								break;
							}
						}
						if (!isFieldRaw) {
							break;
						}
					}
					if (isFieldRaw) {
						sb.append("\t\tpublic " + aCalssName + "(" + getFiendsStr(fieldPair) + "){\n");
						for (Map.Entry<String, String> bEntry : fieldPair.entrySet()) {
							String fieldName = bEntry.getKey();
							String fieldType = bEntry.getValue();
							sb.append("\t\t\tthis." + fieldName + "=p" + fieldName + ";\n");
						}
						sb.append("\t\t}\n");
						sb.append("\t\tpublic " + aCalssName + "(){}\n");
					}
				}
			}
			// 请求函数处理方法
			if (aObj.get("is_req") != null && (boolean) aObj.get("is_req")) {
				int msgCode = (int) aObj.get("id");
				if (REQ_ALL_PROTOCOL_ID_MAP.containsKey(msgCode)) {
					System.err.println("msgCode:" + msgCode + " repeat!!!! please check...");
					throw new RuntimeErrorException(null, "msgCode:" + msgCode + " repeat!!!!");
				}
				REQ_ALL_PROTOCOL_ID_MAP.put(msgCode, msgCode);
				sb.append("\t\tpublic static final int id = " + msgCode + ";\n");
				sb.append("\n");
				sb.append("\t\tpublic static " + aCalssName + " parse(RequestMessageRaw request){\n");
				sb.append("\t\t\t" + aCalssName + " retObj = new " + aCalssName + "();\n");
				sb.append("\t\t\ttry{\n");
				// 读取字段
				if (fieldsObj != null && fieldsObj.size() > 0) {
					for (Map<String, String> aFileConfig : fieldsObj) {
						aFileConfig.remove("desc");
						for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
							String fieldName = bEntry.getKey();
							String fieldType = bEntry.getValue();
							sb.append(getReadStr(templateMap, fieldName, fieldType, true, "i"));
						}
					}
				}
				sb.append("\t\t\t}catch(Exception e){e.printStackTrace();}\n");
				sb.append("\t\t\treturn retObj;\n");
				sb.append("\t\t}\n");
			}
			// 返回函数处理
			if (aObj.get("is_resp") != null && (boolean) aObj.get("is_resp")) {
				sb.append("\t\tpublic SendToMessageRaw build(ByteBufAllocator alloc){\n");
				int msgCode = (int) aObj.get("id");
				sb.append("\t\t\ttry{\n");
				sb.append("\t\t\tSendToMessageRaw retMsg = new SendToMessageRaw(alloc, " + msgCode + ");\n");
				// 读取字段
				if (fieldsObj != null && fieldsObj.size() > 0) {
					for (Map<String, String> aFileConfig : fieldsObj) {
						aFileConfig.remove("desc");
						for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
							String fieldName = bEntry.getKey();
							String fieldType = bEntry.getValue();
							String writeAFieldStr = getWriteStr(templateMap, fieldType, fieldName, "i", true);
							sb.append(writeAFieldStr);
						}
					}
				}
				sb.append("\t\t\treturn retMsg;\n");
				sb.append("\t\t\t}catch(Exception e){e.printStackTrace();return null;}\n");
				sb.append("\t\t}\n");
			}
			sb.append("\t}\n");
		}
		sb.append("}\n");
		// 创建文件对象
		File fileText = new File("E:\\protocols\\java\\LuaMessage" + fileName + ".java");
		// 向文件写入对象写入信息
		FileWriter fileWriter = new FileWriter(fileText);

		// 写文件
		fileWriter.write(sb.toString());
		// 关闭
		fileWriter.close();
		// FileUtils.write(new File("E:\\protocols\\java\\LuaMessage" + fileName
		// + ".java"), sb.toString(),
		// Charset.forName("UTF-8"));
	}

	private static String getFiendsStr(Map<String, String> fieldPair) {
		String ret = "";
		List<String> fieldsList = new ArrayList<>();
		for (Map.Entry<String, String> aEntry : fieldPair.entrySet()) {
			String fieldKey = aEntry.getKey();
			String fieldVal = aEntry.getValue();
			String javaTypeName = getJavaTypeName(fieldVal);
			fieldsList.add(javaTypeName + " p" + fieldKey);
		}
		return StringUtils.join(fieldsList, ",");
	}

	private static boolean isFieldRaw(String fieldType) {
		boolean ret = false;
		if (fieldType.equals("int") || fieldType.equals("byte") || fieldType.equals("long") || fieldType.equals("bool")
				|| fieldType.equals("float") || fieldType.equals("short") || fieldType.equals("string")) {
			ret = true;
		}
		return ret;
	}

	private static String getReadStr(Map<String, Object> rootMap, String fieldName, String fieldType,
			boolean checkExist, String indexKey) {
		String ret = "";
		if (fieldType.equals("int")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readInt();\n";
		} else if (fieldType.equals("byte")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readByte();\n";
		} else if (fieldType.equals("long")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readLong();\n";
		} else if (fieldType.equals("short")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readShort();\n";
		} else if (fieldType.equals("bool")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readBool();\n";
		} else if (fieldType.equals("float")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readFloat();\n";
		} else if (fieldType.equals("string")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readString();\n";
		} else if (fieldType.equals("bytearray")) {
			ret = "\t\t\tretObj." + fieldName + "=request.readByteArray();\n";
		} else if (fieldType.startsWith("[]")) {
			fieldType = fieldType.substring(2);
			String fieldNameNew = StringUtils.replace(fieldName, ".", "_");
			fieldNameNew = StringUtils.remove(fieldNameNew, "[");
			fieldNameNew = StringUtils.remove(fieldNameNew, "]");
			ret += "\t\t\tint " + fieldNameNew + "_size = request.readInt();\n";
			// ret += "\t\t\tif(" + fieldNameNew + "_size >0){\n";
			ret += "\t\t\t\tretObj." + fieldName + " = new " + getJavaTypeName(fieldType) + "[" + fieldNameNew
					+ "_size];\n";
			ret += "\t\t\t\tfor(int " + indexKey + "=0;" + indexKey + "<" + fieldNameNew + "_size;" + indexKey
					+ "++){\n";
			ret += "\t\t" + getReadStr(rootMap, fieldName + "[" + indexKey + "]", fieldType, false, fieldName + "_idx");
			ret += "\t\t\t\t}\n";

			// ret += "\t\t\t}\n";
		} else {
			if (checkExist) {
				ret += "\t\t\tboolean " + fieldName + "_exist = request.readBool();\n";
				ret += "\t\t\tif(" + fieldName + "_exist){\n";
			}
			ret += "\t\t\t\tretObj." + fieldName + " = new " + fieldType + "();\n";

			Map<String, Object> beanObj = (Map<String, Object>) rootMap.get(fieldType);
			List<Map<String, String>> fieldsObj = (List<Map<String, String>>) beanObj.get("fields");
			// 字段
			if (fieldsObj != null && fieldsObj.size() > 0) {
				for (Map<String, String> aFileConfig : fieldsObj) {
					aFileConfig.remove("desc");
					for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
						String bFieldName = bEntry.getKey();
						String bFieldType = bEntry.getValue();
						ret += "\t\t" + getReadStr(rootMap, fieldName + "." + bFieldName, bFieldType, true,
								bFieldName + "_idx");
					}
				}
			}
			if (checkExist) {
				ret += "\t\t\t}\n";
			}
		}
		return ret;
	}

	private static String getWriteStr(Map<String, Object> rootMap, String fieldType, String fieldName, String indexKey,
			boolean needCheckObj) {
		String ret = "";
		if (fieldType.equals("int")) {
			ret = "\t\t\tretMsg.writeInt(this." + fieldName + ");\n";
		} else if (fieldType.equals("byte")) {
			ret = "\t\t\tretMsg.writeByte(this." + fieldName + ");\n";
		} else if (fieldType.equals("short")) {
			ret = "\t\t\tretMsg.writeShort(this." + fieldName + ");\n";
		} else if (fieldType.equals("long")) {
			ret = "\t\t\tretMsg.writeLong(this." + fieldName + ");\n";
		} else if (fieldType.equals("bool")) {
			ret = "\t\t\tretMsg.writeBool(this." + fieldName + ");\n";
		} else if (fieldType.equals("float")) {
			ret = "\t\t\tretMsg.writeFloat(this." + fieldName + ");\n";
		} else if (fieldType.equals("string")) {
			ret = "\t\t\tretMsg.writeString(this." + fieldName + ");\n";
		} else if (fieldType.equals("bytearray")) {
			ret = "\t\t\tretMsg.writeByteArray(this." + fieldName + ");\n";
		} else if (fieldType.startsWith("[]")) {
			fieldType = fieldType.substring(2);
			ret += "\t\t\tif(this." + fieldName + " == null || this." + fieldName + ".length == 0){\n";
			ret += "\t\t\t\tretMsg.writeInt(0);\n";
			ret += "\t\t\t}else{\n";
			ret += "\t\t\t\tretMsg.writeInt(this." + fieldName + ".length);\n";

			ret += "\t\t\t\tfor(int " + indexKey + "=0;" + indexKey + "<this." + fieldName + ".length;" + indexKey
					+ "++){\n";
			ret += getWriteStr(rootMap, fieldType, fieldName + "[" + indexKey + "]", fieldName + "_idx", false);
			ret += "\t\t\t\t}\n";

			ret += "\t\t\t}\n";
		} else {
			if (needCheckObj) {
				ret += "\t\t\tif(this." + fieldName + " == null){\n";
				ret += "\t\t\t\tretMsg.writeBool(false);\n";
				ret += "\t\t\t}else{\n";
				ret += "\t\t\t\tretMsg.writeBool(true);\n";
			}
			Map<String, Object> beanObj = (Map<String, Object>) rootMap.get(fieldType);
			List<Map<String, String>> fieldsObj = (List<Map<String, String>>) beanObj.get("fields");
			// 字段
			if (fieldsObj != null && fieldsObj.size() > 0) {
				for (Map<String, String> aFileConfig : fieldsObj) {
					aFileConfig.remove("desc");
					for (Map.Entry<String, String> bEntry : aFileConfig.entrySet()) {
						String bFieldName = bEntry.getKey();
						String bFieldType = bEntry.getValue();
						ret += "\t\t" + getWriteStr(rootMap, bFieldType, fieldName + "." + bFieldName,
								bFieldName + "_idx", true);
					}
				}
			}
			if (needCheckObj) {
				ret += "\t\t\t}\n";
			}
		}
		return ret;
	}

	private static String getJavaTypeName(String fieldType) {
		String ret = "";
		if (fieldType.equals("int")) {
			ret = "int";
		} else if (fieldType.equals("byte")) {
			ret = "byte";
		} else if (fieldType.equals("short")) {
			ret = "short";
		} else if (fieldType.equals("long")) {
			ret = "long";
		} else if (fieldType.equals("bool")) {
			ret = "boolean";
		} else if (fieldType.equals("float")) {
			ret = "float";
		} else if (fieldType.equals("string")) {
			ret = "String";
		} else if (fieldType.equals("bytearray")) {
			ret = "ByteBuf";
		} else if (fieldType.startsWith("[]")) {
			fieldType = fieldType.substring(2);
			ret = getJavaTypeName(fieldType) + "[]";
		} else {
			ret = fieldType;
		}
		return ret;
	}

	public static String upperCase(String str) {
		byte[] ch = str.getBytes();
		if (ch[0] >= 'a' && ch[0] <= 'z') {
			ch[0] = (byte) (ch[0] - 32);
		}
		return new String(ch);
	}
}
